package com.item.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.base.cache.CacheSupport;
import com.base.exception.SystemException;
import com.base.util.SpringContextUtil;

public class ClassUtils {
	private static Log logger = LogFactory.getLog(ClassUtils.class);
	
	public static Object invokeMethod(String className, String methodName, Map<String, Object> params) throws Exception {
		Object service = SpringContextUtil.getBean(className);

		Class cls = service.getClass();

		Object result = null;

		boolean hasMethod = false;

		for (Method method : cls.getDeclaredMethods()) {
			if (!method.getName().equals(methodName))
				continue;
			hasMethod = true;

			if (method.getParameterTypes().length == 0) {
				result = method.invoke(service, new Object[0]);
				break;
			}
			if (method.getParameterTypes().length <= 0)
				break;
			//得到该方法所有的参数及其类型列表
			List methodParamList = getMethodParams(method);
			if (methodParamList.size() == 1) {
				Object param = null;
				
				String methodParamName = (String) ((Object[]) methodParamList.get(0))[0];

				Class methodParamType = (Class) ((Object[]) methodParamList.get(0))[1];

				if ((!isSimpleClz(methodParamType)) && (!Collection.class.isAssignableFrom(methodParamType))
						&& (!params.containsKey(methodParamName))) {
					param = BeanUtils.convert(params, methodParamType);
				} else {
					param = BeanUtils.convert(params.get(methodParamName), methodParamType);
				}

				result = method.invoke(service, new Object[] { param });
				break;
			}

			Object[] param = new Object[methodParamList.size()];

			for (int i = 0; i < methodParamList.size(); i++) {
				String methodParamName = (String) ((Object[]) methodParamList.get(i))[0];
				
				Class methodParamType = (Class) ((Object[]) methodParamList.get(i))[1];
				
				if (!params.containsKey(methodParamName)) {
					
					param[i] = null;
				} else {
					param[i] = BeanUtils.convert(params.get(methodParamName), methodParamType);
				}
			}
			result = method.invoke(service, param);

			break;
		}

		if (!hasMethod) {
			throw new SystemException("类:" + className + "中没有找到方法:" + methodName);
		}

		return result;
	}


	public static List<Object[]> getMethodParams(Method method) throws Exception {
		ClassPool pool = ClassPool.getDefault();
		pool.insertClassPath(new ClassClassPath(ClassUtils.class));

		CtMethod cm = pool.getMethod(filterCGLIB(method.getDeclaringClass().getName()), method.getName());

		CodeAttribute codeAttribute = cm.getMethodInfo().getCodeAttribute();

		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute("LocalVariableTable");

		List paramList = new ArrayList(cm.getParameterTypes().length);

		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1; //判断有没有this参数

		for (int i = 0; i < cm.getParameterTypes().length; i++) {
			Object[] param = new Object[2];
			param[0] = attr.variableName(i + pos);
			param[1] = method.getParameterTypes()[i];
			paramList.add(param);
		}

		return paramList;
	}

	private static String filterCGLIB(String className) {
		if (className.indexOf("$$EnhancerByCGLIB$$") > -1) {
			className = className.substring(0, className.indexOf("$$EnhancerByCGLIB$$"));
		}
		return className;
	}

	public static List<Method> getClassMethodByAnnotation(Class clz, Class annoClz) throws Exception {
		List result = (List) CacheSupport.get("frameCache", "ClassUtils.getClassMethodByAnnotation" + clz.getName() + annoClz.getName(),
				List.class);
		if (result == null) {
			clz = Class.forName(filterCGLIB(clz.getName()));

			result = new ArrayList();

			for (Method method : clz.getMethods()) {
				if (method.getAnnotation(annoClz) != null) {
					result.add(clz.getMethod(method.getName(), method.getParameterTypes()));
				}
			}

			CacheSupport.put("frameCache", "ClassUtils.getClassMethodByAnnotation" + clz.getName() + annoClz.getName(), result);
		}

		return result;
	}

	public static Map<String, Class> getClassPropertyType(Class clz) {
		Map result = (Map) CacheSupport.get("frameCache", "ClassUtils.getClassPropertyType" + clz.getName(), Map.class);
		if (result == null) {
			result = new HashMap();

			if (clz.getSuperclass() != null) {
				result.putAll(getClassPropertyType(clz.getSuperclass()));
			}

			for (Field field : clz.getDeclaredFields()) {
				result.put(field.getName(), field.getType());
			}

			CacheSupport.put("frameCache", "ClassUtils.getClassPropertyType" + clz.getName(), result);
		}

		return result;
	}

	public static boolean isSimpleClz(Class clz) {
		return (clz.equals(String.class)) || (clz.equals(Long.class)) || (clz.equals(BigDecimal.class)) || (clz.equals(Integer.class))
				|| (clz.equals(Integer.TYPE)) || (clz.equals(Long.TYPE)) || (clz.equals(Float.class)) || (clz.equals(Float.TYPE));
	}

	public static String tableName2ClassName(String tableName) {
		String result = "";

		if (!StringUtils.isBlank(tableName)) {
			String[] tableNameArray = tableName.toLowerCase().split("_");

			for (int i = 0; i < tableNameArray.length; i++) {
				result = result + tableNameArray[i].substring(0, 1).toUpperCase() + tableNameArray[i].substring(1);
			}
		}

		return result;
	}
}